JBoss Community Archive (Read Only)

ModeShape 3

Query and search

The JCR API defines a way to query a repository for content that meets user-defined criteria. The JCR 2.0 API actually makes it possible for implementations to support multiple query languages, and the specification requires support for two languages: JCR-SQL2 and JCR-QOM. JCR 1.0 defined two other languages (XPath and JCR-SQL), though these languages were deprecated in JCR 2.0.

Choosing a query language

At this time, ModeShape supports five query languages:

  • JCR-SQL2

  • JCR-SQL

  • XPath

  • JCR-JQOM (programmatic API)

  • full-text search (a language that reuses the full-text search expression grammar used in the second parameter of the CONTAINS(...) function of the JCR-SQL2 language)

So which language should you choose?

You might think you should pick one based upon how quickly it can be executed, but that's not the case with ModeShape. As well see below, ModeShape plans, optimizes, and executes all the Query instances in the exact same way, regardless of how they were created.

With ModeShape, none of the languages are more or less efficient than the others. So the best reason to pick one language over another is based upon the expressiveness of the language. In other words, pick the language for each query that most easily expresses your application's needs! Sure, the JCR-SQL2 language is by far the most expressive, and is technically a superset of JCR-SQL. But sometimes it will be easier to specify path-oriented criteria using XPath. Or sometimes you only need to do full-text search, in which case ModeShape's full-text search language is far easier.

Not all JCR implementations execute their queries in the same way. Some (including Jackrabbit) have completely different execution paths for different languages, meaning queries in some languages are simply faster than equivalent queries expressed in other languages.

Creating queries

There are two ways to create a JCR Query object. The first is by supplying a query expression and the name of the query language, and this can be done with the standard JCR API:

// Obtain the query manager for the session via the workspace ...
javax.jcr.query.QueryManager queryManager = session.getWorkspace().getQueryManager();

// Create a query object ...
String language = ...  // e.g. javax.jcr.query.Query.JCR_SQL2
String expression = ...
javax.jcr.query.Query query = queryManager.createQuery(expression,language);

Before returning the Query, ModeShape finds a parser for the language given by the language parameter, and uses this parser to create a language-independent object representation of the query. (Note that any grammatical errors in the expression result in an immediate exception.) This object representation is what JCR 2.0 calls the "Query Object Model", or QOM. After parsing, ModeShape embeds the QOM into the Query object.

The second approach for creating a Query object is to programmatically build up the query using the QueryObjectModelFactory. Again, this uses the standard JCR API. Here's a simple example:

// Obtain the query manager for the session via the workspace ...
javax.jcr.query.QueryManager queryManager = session.getWorkspace().getQueryManager();
javax.jcr.query.qom.QueryObjectModelFactory factory = queryManager.getQOMFactory();

// Create the parts of a query object ...
javax.jcr.query.qom.Source selector = factory.selector(...);
javax.jcr.query.qom.Constraint constraints = ...
javax.jcr.query.qom.Column[] columns = ...
javax.jcr.query.qom.Ordering[] orderings = ...
javax.jcr.query.qom.QueryObjectModel model =
    factory.createQuery(selector,constraints,orderings,columns);

// The model is a query ...
javax.jcr.query.Query query = model;

Of course, the QueryObjectModelFactory can create lots variations of selectors, joins, constraints, and orderings. ModeShape fully supports this style of creating queries, and it even offers some very useful extensions (described below).

Executing queries

As we mentioned above, all ModeShape Query objects contain the object representation of the query, called the query object model. No matter which query language is used or whether the query was created programmatically, the ModeShape uses the same kind of model objects to represent every single query.

So when the JCR client executes the query:

javax.jcr.query.Query query = ...

// Execute the query and get the results ...
javax.jcr.query.QueryResult result = query.execute();

ModeShape then takes the query's object model and runs it through a series of steps to plan, validate, optimize, and finally execute the query:

  1. Planning - in this step, ModeShape converts the language-independent query object model into a canonical relational query plan that outlines the various relational operations that need to be performed for this query. The query plan forms a tree, with each leaf node representing an access query against the indexes. However, this plan isn't quite ready to be used.

  2. Validation - not all queries that are well-formed can be executed, so ModeShape then validates the canonical query plan to make sure that all named selectors exist, all named properties exist on the selectors, that all aliases are properly used, and that all identifiers are resolvable. If the query fails validation, an exception is thrown immediately.

  3. Optimization - the canonical plan should mirror the actual query model, but it may not be the most simple or efficient plan. ModeShape runs the canonical plan through a rule-based optimizer to produce an optimum and executable plan. For example, one rule rewrites right outer joins as left outer joins. Another rule looks for identity joins (e.g., ISSAMENODE join criteria or equi-join criteria involving node identifiers), and if possible removes the join altogether (replacing it with additional criteria) or copies criteria on one side of the join to the other. Another rule removes parts of the plan that (based upon criteria) will never return any rows. Yet another rule determines the best algorithm for joining tuples. Overall, there are about a dozen such rules, and all are intended to make the query plans more easily and efficiently executed.

  4. Execution - the optimized plan is then executed: each access query in the plan is issued and the resulting tuples processed and combined to form the result set's tuples.

Now that you know more about how ModeShape actually works, you can understand why ModeShape can achieve such good query performance regardless of the language you choose to use.

SQL and JCR-SQL2 extensions

ModeShape adds several features to its support of the standard JCR-SQL and JCR-SQL2 grammars. These extensions include support for:

  1. Additional join types with FULL OUTER JOIN and CROSS JOIN

  2. UNION, INTERSECT, and EXCEPT set operations

  3. Non-correlated subqueries in the WHERE clause; multiple subqueries can be used in a single query, and they can even be nested

  4. Removing duplicate rows with SELECT DISTINCT ...

  5. Limit the number of rows returned with LIMIT count

  6. Skip initial rows with OFFSET number

  7. Constrain the depth of a node with DEPTH(selectorName)

  8. Constrain the path of a node with PATH(selectorName)

  9. Constrain the references from a node with REFERENCE(selectorName.property) and REFERENCE(selectorName)

  10. Ranges of criteria values using BETWEEN lower AND upper and optionally specifying whether to exclude the lower and/or upper values

  11. Set criteria to specify multiple criteria values using IN and NOT IN

  12. Use simple arithmetic in criteria and ORDER BY clauses, such as SCORE(type1)*3 + SCORE(type2)

  13. Use pseudo-columns to include the path, score, node name, node local name, and node depth in result columns or in criteria

More detail of the particular extensions can be found in the JCR-SQL2 grammar.

Simply use these extensions within your JCR-SQL or JCR-SQL2 query expressions strings, and use the standard JCR API to obtain a Query:

// Obtain the query manager for the session via the workspace ...
javax.jcr.query.QueryManager queryManager = session.getWorkspace().getQueryManager();

// Create a query object ...
String language = ...
String expression = ...  // USE THE EXTENSIONS HERE
javax.jcr.query.Query query = queryManager.createQuery(expression,language);

// And use the query ...

Query Object Model extensions

The extensions in the JCR-SQL and JCR-SQL2 languages can also be used when building queries programmatically using the JCR Query Object Model API. ModeShape defines as part of its public API the org.modeshape.jcr.api.query.qom.QueryObjectModelFactory interface that extends the standard javax.jcr.query.qom.QueryObjectModelFactory interface, and which contains methods providing ways to construct a QOM with the extended features.

Additional join types

The standard javax.jcr.query.qom.QueryObjectModelFactory interface uses a String to specify the join type:

package javax.jcr.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Performs a join between two node-tuple sources.
     *
     * The query is invalid if 'left' is the same source as 'right'.
     *
     * @param left the left node-tuple source; non-null
     * @param right the right node-tuple source; non-null
     * @param joinType either QueryObjectModelConstants.JCR_JOIN_TYPE_INNER,
     *        QueryObjectModelConstants.JCR_JOIN_TYPE_LEFT_OUTER, or
     *        QueryObjectModelConstants.JCR_JOIN_TYPE_RIGHT_OUTER.
     * @param joinCondition the join condition; non-null
     * @return the join; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later,
     *         on {@link #createQuery}), and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public Join join( Source left,
                      Source right,
                      String joinType,
                      JoinCondition joinCondition ) throws InvalidQueryException, RepositoryException;
    ...
}

In addition to the three standard constants, ModeShape supports two additional constant values:

  • javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_INNER

  • javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_LEFT_OUTER

  • javax.jcr.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_RIGHT_OUTER

  • org.modeshape.jcr.api.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_CROSS

  • org.modeshape.jcr.api.query.qom.QueryObjectModelConstants.JCR_JOIN_TYPE_FULL_OUTER

Set operations with UNION, INTERSECT, and EXCEPT

Creating a set query is very similar to creating a normal SELECT type query, but instead the following on org.modeshape.jcr.api.query.qom.QueryObjectModelFactory are used:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Creates a query with one or more selectors.
     *
     * @param source the node-tuple source; non-null
     * @param constraint the constraint, or null if none
     * @param orderings zero or more orderings; null is equivalent to a zero-length array
     * @param columns the columns; null is equivalent to a zero-length array
     * @param limit the limit; null is equivalent to having no limit
     * @param isDistinct true if the query should return distinct values; or false if no
     *        duplicate removal should be performed
     * @return the select query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail that
     *         test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SelectQuery select( Source source,
                               Constraint constraint,
                               Ordering[] orderings,
                               Column[] columns,
                               Limit limit,
                               boolean isDistinct ) throws InvalidQueryException, RepositoryException;

    /**
     * Creates a query command that effectively appends the results of the right-hand query
     * to those of the left-hand query.
     *
     * @param left the query command that represents left-side of the set operation;
     *        non-null and must have columns that are equivalent and union-able to those
     *        of the right-side query
     * @param right the query command that represents right-side of the set operation;
     *        non-null and must have columns that are equivalent and union-able to those
     *        of the left-side query
     * @param orderings zero or more orderings; null is equivalent to a zero-length array
     * @param limit the limit; null is equivalent to having no limit
     * @param all true if duplicate rows in the left- and right-hand side results should
     *        be included, or false if duplicate rows should be eliminated
     * @return the select query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail
     *         that test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SetQuery union( QueryCommand left,
                           QueryCommand right,
                           Ordering[] orderings,
                           Limit limit,
                           boolean all ) throws InvalidQueryException, RepositoryException;

    /**
     * Creates a query command that returns all rows that are both in the result of the
     * left-hand query and in the result of the right-hand query.
     *
     * @param left the query command that represents left-side of the set operation;
     *        non-null and must have columns that are equivalent and union-able to those
     *        of the right-side query
     * @param right the query command that represents right-side of the set operation;
     *        non-null and must have columns that are equivalent and union-able to those
     *        of the left-side query
     * @param orderings zero or more orderings; null is equivalent to a zero-length array
     * @param limit the limit; null is equivalent to having no limit
     * @param all true if duplicate rows in the left- and right-hand side results should
     *        be included, or false if duplicate rows should be eliminated
     * @return the select query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail
     *         that test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SetQuery intersect( QueryCommand left,
                               QueryCommand right,
                               Ordering[] orderings,
                               Limit limit,
                               boolean all ) throws InvalidQueryException, RepositoryException;

    /**
     * Creates a query command that returns all rows that are in the result of the left-hand
     * query but not in the result of the right-hand query.
     *
     * @param left the query command that represents left-side of the set operation;
     *        non-null and must have columns that are equivalent and union-able to those
     *        of the right-side query
     * @param right the query command that represents right-side of the set operation;
     *        non-null and must have columns that are equivalent and union-able to those
     *        of the left-side query
     * @param orderings zero or more orderings; null is equivalent to a zero-length array
     * @param limit the limit; null is equivalent to having no limit
     * @param all true if duplicate rows in the left- and right-hand side results should
     *        be included, or false if duplicate rows should be eliminated
     * @return the select query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail
     *         that test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SetQuery except( QueryCommand left,
                            QueryCommand right,
                            Ordering[] orderings,
                            Limit limit,
                            boolean all ) throws InvalidQueryException, RepositoryException;
    ...
}

Note that the select(...) method returns a SelectQuery while the union(...), intersect(...) and except(...) methods return a SetQuery. The SelectQuery and SetQuery interfaces are defined by ModeShape and both extend ModeShape's QueryCommand interface. This interface is then used in the methods to create SetQuery.

The SetQuery object is not executable. To create the corresponding javax.jcr.Query object, pass the SetQuery to the following method on org.modeshape.jcr.api.query.qom.QueryObjectModelFactory:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Creates a set query.
     *
     * @param command set query; non-null
     * @return the executable query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail
     *         that test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SetQueryObjectModel createQuery( SetQuery command ) throws InvalidQueryException, RepositoryException;
    ...
}

The resulting SetQueryObjectModel extends javax.jcr.query.Query and SetQuery and can be executed and treated similarly to the standard javax.jcr.query.qom.QueryObjectModel (that also extends javax.jcr.query.Query).

correlated subqueries

ModeShape defines a Subquery interface that extends the standard javax.jcr.query.qom.StaticOperand interface, and thus can be used on the right-hand side of any Criteria:

public interface Subquery extends StaticOperand {
    /**
     * Gets the {@link QueryCommand} that makes up the subquery.
     *
     * @return the query command; non-null
     */
    public QueryCommand getQuery();
}

Subqueries can be created by passing a QueryCommand into this org.modeshape.jcr.query.qom.QueryObjectModelFactory method:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Creates a subquery that can be used as a {@link StaticOperand} in another query.
     *
     * @param subqueryCommand the query command that is to be used as the subquery
     * @return the constraint; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later,
     *         on {@link #createQuery}), and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public Subquery subquery( QueryCommand subqueryCommand ) throws InvalidQueryException, RepositoryException;
    ...
}

The resulting Subquery is a StaticOperand that can then be used to create a Criteria.

Removing duplicate rows

The org.modeshape.jcr.query.qom.QueryObjectModelFactory interface includes a variation of the standard QueryObjectModeFactory.select(...)
method with an additional isDistinct flag that controls whether duplicate rows should be removed:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Creates a query with one or more selectors.
     *
     * @param source the node-tuple source; non-null
     * @param constraint the constraint, or null if none
     * @param orderings zero or more orderings; null is equivalent to a zero-length array
     * @param columns the columns; null is equivalent to a zero-length array
     * @param limit the limit; null is equivalent to having no limit
     * @param isDistinct true if the query should return distinct values; or false if no
     *        duplicate removal should be performed
     * @return the select query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail
     *         that test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SelectQuery select( Source source,
                               Constraint constraint,
                               Ordering[] orderings,
                               Column[] columns,
                               Limit limit,
                               boolean isDistinct ) throws InvalidQueryException, RepositoryException;
    ...
}

Limit and offset results

ModeShape defines a Limit interface as a top-level object that can be used to create queries that limit the number of rows and/or skip a number of initial rows:

public interface Limit {

    /**
     * Get the number of rows skipped before the results begin.
     *
     * @return the offset; always 0 or a positive number
     */
    public int getOffset();

    /**
     * Get the maximum number of rows that are to be returned.
     *
     * @return the maximum number of rows; always positive, or equal to Integer.MAX_VALUE if there is no limit
     */
    public int getRowLimit();

    /**
     * Determine whether this limit clause is necessary.
     *
     * @return true if the number of rows is not limited and there is no offset, or false otherwise
     */
    public boolean isUnlimited();

    /**
     * Determine whether this limit clause defines an offset.
     *
     * @return true if there is an offset, or false if there is no offset
     */
    public boolean isOffset();
}

These range constraints can be constructed using this org.modeshape.jcr.query.qom.QueryObjectModelFactory method:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Evaluates to a limit on the maximum number of tuples in the results and the
     * number of rows that are skipped before the first tuple in the results.
     *
     * @param rowLimit the maximum number of rows; must be a positive number, or Integer.MAX_VALUE if there is to be a
     *        non-zero offset but no limit
     * @param offset the number of rows to skip before beginning the results; must be 0 or a positive number
     * @return the operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public Limit limit( int rowLimit,
                        int offset ) throws InvalidQueryException, RepositoryException;
    ...
}

The Limit objects can then be used when creating queries using a variation of the standard QueryObjectModeFactory.select(...) defined in the org.modeshape.jcr.query.qom.QueryObjectModelFactory interface:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Creates a query with one or more selectors.
     *
     * @param source the node-tuple source; non-null
     * @param constraint the constraint, or null if none
     * @param orderings zero or more orderings; null is equivalent to a zero-length array
     * @param columns the columns; null is equivalent to a zero-length array
     * @param limit the limit; null is equivalent to having no limit
     * @param isDistinct true if the query should return distinct values; or false if no
     *        duplicate removal should be performed
     * @return the select query; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test and the parameters given fail
     *         that test. See the individual QOM factory methods for the validity criteria
     *         of each query element.
     * @throws RepositoryException if another error occurs.
     */
    public SelectQuery select( Source source,
                               Constraint constraint,
                               Ordering[] orderings,
                               Column[] columns,
                               Limit limit,
                               boolean isDistinct ) throws InvalidQueryException, RepositoryException;
    ...
}

Similarly, the Limit objects can be passed to the ModeShape-specific except(...), union(...), intersect(...) methods, too.

Depth constraints

ModeShape defines a DepthPath interface that extends the standard javax.jcr.query.qom.DynamicOperand interface, and thus can be used as part of a WHERE clause to constrain the depth of the nodes accessed by a selector:

public interface NodeDepth extends javax.jcr.query.qom.DynamicOperand {

    /**
     * Get the selector symbol upon which this operand applies.
     *
     * @return the one selector names used by this operand; never null
     */
    public String getSelectorName();
}

These range constraints can be constructed using this org.modeshape.jcr.query.qom.QueryObjectModelFactory method:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Evaluates to a LONG value equal to the depth of a node in the specified selector.
     *
     * The query is invalid if selector is not the name of a selector in the query.
     *
     * @param selectorName the selector name; non-null
     * @return the operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public NodeDepth nodeDepth( String selectorName ) throws InvalidQueryException, RepositoryException;
    ...
}

Path constraints

ModeShape defines a NodePath interface that extends the standard javax.jcr.query.qom.DynamicOperand interface, and thus can be used as part of a WHERE clause to constrain the path of nodes accessed by a selector:

public interface NodePath extends javax.jcr.query.qom.DynamicOperand {

    /**
     * Get the selector symbol upon which this operand applies.
     *
     * @return the one selector names used by this operand; never null
     */
    public String getSelectorName();
}

These range constraints can be constructed using this org.modeshape.jcr.query.qom.QueryObjectModelFactory method:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Evaluates to a PATH value equal to the prefix-qualified path of a node in the specified selector.
     *
     * The query is invalid if selector is not the name of a selector in the query.
     *
     * @param selectorName the selector name; non-null
     * @return the operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public NodePath nodePath( String selectorName ) throws InvalidQueryException, RepositoryException;
    ...
}

Criteria on references from a node

ModeShape defines a ReferenceValue interface that extends the standard javax.jcr.query.qom.DynamicOperand interface, and thus can be used as part of a WHERE or ORDER BY clause:

public interface ReferenceValue extends DynamicOperand {
    ...
    /**
     * Get the selector symbol upon which this operand applies.
     *
     * @return the one selector names used by this operand; never null
     */
    public String getSelectorName();

    /**
     * Get the name of the one reference property.
     *
     * @return the property name; or null if this operand applies to any reference property
     */
    public String getPropertyName();
}

These reference value operand allow a query to easily place constraints on a particular REFERENCE property or (more importantly) any REFERENCE properties on the nodes. The former is a more simple alternative to using a regular comparison constraint with the REFERENCE property on one side and the "jcr:uuid" property on the other. The latter effectively means "where the node references (with any property) some other nodes", and this is something that standard JCR-SQL2 cannot represent.

They are created using these org.modeshape.jcr.query.qom.QueryObjectModelFactory methods:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Creates a dynamic operand that evaluates to the REFERENCE value of the any property
     * on the specified selector.
     *
     * The query is invalid if:
     * - selector is not the name of a selector in the query, or
     * - property is not a syntactically valid JCR name.
     *
     * @param selectorName the selector name; non-null
     * @return the operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public ReferenceValue referenceValue( String selectorName ) throws InvalidQueryException, RepositoryException;

    /**
     * Creates a dynamic operand that evaluates to the REFERENCE value of the specified
     * property on the specified selector.
     *
     * The query is invalid if:
     * - selector is not the name of a selector in the query, or
     * - property is not a syntactically valid JCR name.
     *
     * @param selectorName the selector name; non-null
     * @param propertyName the reference property name; non-null
     * @return the operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public ReferenceValue referenceValue( String selectorName,
                                          String propertyName ) throws InvalidQueryException, RepositoryException;
    ...
}

Range criteria with BETWEEN

ModeShape defines a Between interface that extends the standard javax.jcr.query.qom.Constraint interface, and thus can be used as part of a WHERE clause:

public interface Between extends Constraint {

    /**
     * Get the dynamic operand specification.
     *
     * @return the dynamic operand; never null
     */
    public DynamicOperand getOperand();

    /**
     * Get the lower bound operand.
     *
     * @return the lower bound; never null
     */
    public StaticOperand getLowerBound();

    /**
     * Get the upper bound operand.
     *
     * @return the upper bound; never null
     */
    public StaticOperand getUpperBound();

    /**
     * Return whether the lower bound is to be included in the results.
     *
     * @return true if the {@link #getLowerBound() lower bound} is to be included, or false otherwise
     */
    public boolean isLowerBoundIncluded();

    /**
     * Return whether the upper bound is to be included in the results.
     *
     * @return true if the {@link #getUpperBound() upper bound} is to be included, or false otherwise
     */
    public boolean isUpperBoundIncluded();
}

These range constraints can be constructed using this org.modeshape.jcr.query.qom.QueryObjectModelFactory method:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Tests that the value (or values) defined by the supplied dynamic operand are
     * within a specified range. The range is specified by a lower and upper bound,
     * and whether each of the boundary values is included in the range.
     *
     * @param operand the dynamic operand describing the values that are to be constrained
     * @param lowerBound the lower bound of the range
     * @param upperBound the upper bound of the range
     * @param includeLowerBound true if the lower boundary value is not be included
     * @param includeUpperBound true if the upper boundary value is not be included
     * @return the constraint; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public Between between( DynamicOperand operand,
                            StaticOperand lowerBound,
                            StaticOperand upperBound,
                            boolean includeLowerBound,
                            boolean includeUpperBound ) throws InvalidQueryException, RepositoryException;
    ...
}

To create a NOT BETWEEN ... criteria, simply create the Between criteria object, and then pass that into the standard QueryObjectModelFactory.not(Criteria) method.

Set criteria with IN and NOT IN

ModeShape defines a SetCriteria interface that extends the standard javax.jcr.query.qom.Constraint interface, and thus can be used as part of a WHERE clause:

public interface SetCriteria extends Constraint {

    /**
     * Get the dynamic operand specification for the left-hand side of the set criteria.
     *
     * @return the dynamic operand; never null
     */
    public DynamicOperand getOperand();

    /**
     * Get the static operands for this set criteria.
     *
     * @return the static operand; never null and never empty
     */
    public Collection<? extends StaticOperand> getValues();
}

These set constraints can be constructed using this org.modeshape.jcr.query.qom.QueryObjectModelFactory method:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
   /**
     * Tests that the value (or values) defined by the supplied dynamic operand are
     * found within the specified set of values.
     *
     * @param operand the dynamic operand describing the values that are to be constrained
     * @param values the static operand values; may not be null or empty
     * @return the constraint; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public SetCriteria in( DynamicOperand operand,
                           StaticOperand... values ) throws InvalidQueryException, RepositoryException;
    ...
}

To create a NOT IN criteria, simply create the IN criteria to get a SetCriteria object, and then pass that into the standard QueryObjectModelFactory.not(Criteria) method.

Arithmetic operands

ModeShape defines an ArithmeticOperand interface that extends the javax.jcr.query.qom.DynamicOperand, and thus can be used anywhere a DynamicOperand can be used.

public interface ArithmeticOperand extends DynamicOperand {

    /**
     * Get the operator for this binary operand.
     *
     * @return the operator; never null
     */
    public String getOperator();

    /**
     * Get the left-hand operand.
     *
     * @return the left-hand operator; never null
     */
    public DynamicOperand getLeft();

    /**
     * Get the right-hand operand.
     *
     * @return the right-hand operator; never null
     */
    public DynamicOperand getRight();
}

These can be constructed using additional org.modeshape.jcr.query.qom.QueryObjectModelFactory methods:

package org.modeshape.jcr.api.query.qom;

public interface QueryObjectModelFactory {
    ...
    /**
     * Create an arithmetic dynamic operand that adds the numeric value of the two supplied operand(s).
     *
     * @param left the left-hand-side operand; not null
     * @param right the right-hand-side operand; not null
     * @return the dynamic operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public ArithmeticOperand add( DynamicOperand left,
                                  DynamicOperand right ) throws InvalidQueryException, RepositoryException;

    /**
     * Create an arithmetic dynamic operand that subtracts the numeric value of the second operand from the numeric value of the
     * first.
     *
     * @param left the left-hand-side operand; not null
     * @param right the right-hand-side operand; not null
     * @return the dynamic operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public ArithmeticOperand subtract( DynamicOperand left,
                                       DynamicOperand right ) throws InvalidQueryException, RepositoryException;

    /**
     * Create an arithmetic dynamic operand that multplies the numeric value of the first operand by the numeric value of the
     * second.
     *
     * @param left the left-hand-side operand; not null
     * @param right the right-hand-side operand; not null
     * @return the dynamic operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public ArithmeticOperand multiply( DynamicOperand left,
                                       DynamicOperand right ) throws InvalidQueryException, RepositoryException;

    /**
     * Create an arithmetic dynamic operand that divides the numeric value of the first operand by the numeric value of the
     * second.
     *
     * @param left the left-hand-side operand; not null
     * @param right the right-hand-side operand; not null
     * @return the dynamic operand; non-null
     * @throws InvalidQueryException if a particular validity test is possible on this method,
     *         the implemention chooses to perform that test (and not leave it until later, on createQuery),
     *         and the parameters given fail that test
     * @throws RepositoryException if the operation otherwise fails
     */
    public ArithmeticOperand divide( DynamicOperand left,
                                     DynamicOperand right ) throws InvalidQueryException, RepositoryException;
    ...
}

Search and text extraction

The full-text search language and JCR-SQL2's full-text search constraint both have the ability to find nodes using a simpler search-engine-like expression with wildcards and phrases.

One can pretty easily imagine how ModeShape performs these matches against a node's name and properties containing STRING, LONG, DATE, DOUBLE, DECIMAL, NAME, and PATH values. But what about BINARY values? In order to determine whether the search-engine-like search expressions match, doesn't ModeShape have to determine what text is contained within each BINARY value?

The short answer is that yes, ModeShape can only match against the BINARY value if it can extract the text from that value. And this is where text extraction come into play.

A text extractor is a component that knows how to extract searchable text from a BINARY value. Each text extract describes whether it can process files of a particular MIME type, and if it can then ModeShape will (when necessary) call the extractor to obtain for a supplied BINARY value the searchable text.

JBoss.org Content Archive (Read Only), exported from JBoss Community Documentation Editor at 2020-03-11 12:06:39 UTC, last content change 2013-05-27 21:57:57 UTC.